#include "public.h"
#include "event-internal.h"
#include "log.h"


struct event_base_s *g_event_base = NULL;




static	int	event_base_timer_init( struct event_base_s *base, int nmax )
{
	base->event_timer_size		= nmax;
	base->event_timer_array		= (struct event_s**)malloc(sizeof(struct event_s*)*base->event_timer_size);
	if( NULL == base->event_timer_array )
                return -1;
	
	memset( base->event_timer_array, 0, sizeof(struct event_s*)*base->event_timer_size );
	
	base->event_timer_idx	= 0;
	base->event_cur_time	= time(NULL);
	
	return 0;
}


static void	event_base_timer_shutdown( struct event_base_s *base )
{
	assert( NULL!=base->event_timer_array );

	base->event_timer_size	= 0;
	free( base->event_timer_array);
	base->event_timer_array = NULL;
	base->event_timer_idx	= 0;
	base->event_cur_time    = 0;	
}


static int	event_base_timer_add( struct event_base_s *base, struct event_s *ev, int timeout )
{
	int	index;

	

	timeout	= timeout<0?0:timeout;
	timeout	= timeout>base->event_timer_size?base->event_timer_size:timeout;
	index	= (index + base->event_timer_idx)%base->event_timer_size;
	
	ev->next = base->event_timer_array[index];
	ev->prev = NULL;

	ev->next->prev = ev;
	base->event_timer_array[index] = ev;
	
	ev->timeout = base->event_cur_time + timeout;

	log_debug( base->log, "add timer fd %d timeout %d ", ev->fd, ev->timeout );
	
	return 0;
}



static void	event_base_timer_del( struct event_base_s *base, struct event_s *ev )
{
	int	index;

	
	log_debug( base->log, "delete timer fd %d timeout %d", ev->fd, ev->timeout );
	
	index = (base->event_timer_idx + ev->timeout - base->event_cur_time)%base->event_timer_size;
	if( base->event_timer_array[index] == ev )
		base->event_timer_array[index] = ev->next;
	
	if( ev->prev )
		ev->prev->next = ev->next;
	if( ev->next )
		ev->next->prev = ev->prev;
	
	ev->prev = ev->next = NULL;
}




static void	event_base_timer_process( struct event_base_s *base, time_t t )
{
	int	i, index, timeout;

	timeout = t - base->event_cur_time;
	assert(timeout>=0);

	timeout = (timeout>base->event_timer_size)?base->event_timer_size:timeout;

	for( index = base->event_timer_idx, i = 0;  i < timeout; i++ )
	{
		struct event_s *ev;

		ev = base->event_timer_array[index];
		while( ev )
		{
			event_base_active( base, ev, EV_TIMEOUT );
			ev = ev->next;
		}
		base->event_timer_array[index] = NULL;
		
		index++;
		index = (index>(base->event_timer_size-1))?0:index;
	}
	
	base->event_timer_idx	= index;
	base->event_cur_time	= t;
}



static	int	event_base_array_init( struct event_base_s *base, int nmax )
{
	base->event_total_size          = nmax;
	base->event_total_array         = (struct event_s**)malloc(sizeof(struct event_s*)*base->event_total_size);
	if( NULL == base->event_total_array )
                return -1;
	memset( base->event_total_array, 0, sizeof(struct event_s*)*base->event_total_size );
	
	base->event_active_array        = (struct event_s**)malloc(sizeof(struct event_s*)*base->event_total_size);
	if( NULL == base->event_active_array )
	{
		free(base->event_total_array);
		return -1;
	}
	memset( base->event_active_array, 0, sizeof(struct event_s*)*base->event_total_size );
	
	base->event_total_count		= 0;
	base->event_active_count	= 0;

	return 0;
}


static	void	event_base_array_shutdown( struct event_base_s *base )
{
	assert( NULL!=base->event_total_array );
	assert( NULL!=base->event_active_array );

	
	base->event_total_size		= 0;
	free( base->event_total_array );
	base->event_total_array		= NULL;
	free( base->event_active_array );
	base->event_active_array	= NULL;
	base->event_total_count		= 0;
	base->event_active_count	= 0 ;
}


static	int	event_base_array_add( struct event_base_s *base, struct event_s *ev )
{
	int index;
	
	index = base->event_total_count;
	if( index >= base->event_total_size )
	{
		log_alert( base->log, "add event fd %d index %d size %d", 
			ev->fd, index, base->event_total_size );
		return -1;
	}
	
	base->event_total_array[index] = ev;
        ev->idx = index;
	base->event_total_count++;
	
	log_debug( base->log, "add event fd %d index %d", ev->fd, ev->idx );
	return 0;
}



static	void	event_base_array_del( struct event_base_s *base, struct event_s *ev )
{
	int index;

	log_debug( base->log, "delete event fd %d index %d", ev->fd, ev->idx );
	
	index = base->event_total_count - 1;
	base->event_total_array[ev->idx]	= base->event_total_array[index];
	base->event_total_array[ev->idx]->idx	= ev->idx; 
	base->event_total_array[index]		= NULL;
	base->event_total_count--;
	ev->idx = -1;

	
}



int     event_base_new( int max_event, int max_timer, const char *log_fn )
{
	int			nret;
	struct event_base_s	*base;

	g_event_base = NULL;
	base = (struct event_base_s*)malloc(sizeof(*base));
	if( NULL == base ) 
		return RET_ERR;

	base->event_ops_arg		= NULL;
	base->event_ops			= NULL;

	base->log = log_open( log_fn );
        if( NULL == base->log )
        {
                free(base);
		return RET_ERR;
        }

	nret = event_base_array_init( base, max_event );
	if( 0 != nret )
	{
		log_close( base->log );
		free(base);
		return RET_ERR;
	}

	nret = event_base_timer_init( base, max_timer );
	if( 0 != nret )
	{
		event_base_array_shutdown( base );
		log_close( base->log );
		free(base);
		return RET_ERR;
	}
	
	base->event_ops = NULL;/* select or epoll or iocp */
	base->event_ops_arg = base->event_ops->init( base, base->event_total_size );
	if( NULL == base->event_ops_arg )
	{
		event_base_array_shutdown( base );
		event_base_timer_shutdown( base );
                log_close( base->log );
                free(base);
		return RET_ERR;
	}

	g_event_base = base;	

	return RET_OK;
}



void	event_base_delete(  )
{
	struct event_base_s *base;

	base = g_event_base;
	assert( NULL!=base );
	assert( NULL!=base->event_ops );
	
	base->event_ops->shutdown( base, base->event_ops_arg );
	event_base_timer_shutdown( base );
	event_base_array_shutdown( base );
	log_close( base->log );
	free( base );
	
	g_event_base = NULL;
}


void	event_base_active( struct event_base_s *base, struct event_s *ev, short flags )
{
	
	log_debug( base->log, "active event fd %d size %d count %d", 
		base->event_total_size, base->event_active_count );
	
	if( ev->flags&EV_ACTIVE )
	{
		ev->flags |= flags;
		return;
	}
	
	if( base->event_active_count >= base->event_total_size )
	{
		log_error( g_event_base->log, "fd %d size %d count %d", ev->fd, 
			g_event_base->event_total_size, g_event_base->event_active_count );
		return;
	}
	

	base->event_active_count++;
	ev->flags = flags|EV_ACTIVE;
	base->event_active_array[base->event_active_count - 1] = ev;
	
}


struct event_s* event_base_array_get( struct event_base_s *base, int idx )
{
	if( idx < 0 || idx >= base->event_total_count )
		return NULL;

	return base->event_total_array[idx];
}



struct event_s *event_new()
{
	struct event_s *ev;

	ev = (struct event_s*)malloc( sizeof(*ev) );
	if( NULL == ev ) 
		return NULL;

	return ev;
}


void	event_free( struct event_s *ev )
{
	free( ev );
}


void    event_set( struct event_s *ev, int fd, short events, void  (*handle)( int fd, short flags, void *arg ), void *arg )
{
        ev->fd          = fd;
	ev->timeout	= 0;
        ev->arg         = arg;
        ev->events      = events;
	ev->flags	= EV_INIT;
        ev->handle      = handle;
	ev->idx		= -1;
	ev->prev	= NULL;
	ev->next	= NULL;
	
}



int	event_add( struct event_s *ev, int timeout/* per second */ )
{
	int			index;
	struct event_base_s	*base;
	

	base = g_event_base;

	assert( NULL!=ev );
	assert( !(ev->flags&EV_ACTIVE) );
	

	if( 0 != base->event_ops->add( base, base->event_ops_arg, ev ) )
		return -1;

	if( 0 != event_base_timer_add( base, ev, timeout ) )
	{
		base->event_ops->del( base, base->event_ops_arg, ev );
		return -1;
	}


	if( 0 != event_base_array_add( base, ev) )
	{
		event_base_timer_del( base, ev );
		base->event_ops->del( base, base->event_ops_arg, ev );
		return -1;
	}


	return 0;
}



void	event_del( struct event_s *ev )
{
	struct event_base_s *base;

	base = g_event_base;

	event_base_array_del( base, ev );
	event_base_timer_del( base, ev );

	base->event_ops->del( base, base->event_ops_arg, ev );
}



void	event_dispatch( int timeout )
{
	int			nret, index;
	struct timeval		tv;
	time_t			tnow;
	struct event_base_s	*base;

	
	tv.tv_sec	= timeout;
	tv.tv_usec	= 0;
	base		= g_event_base;
	tnow		= time(NULL);

	nret = base->event_ops->dispatch( base, base->event_ops_arg, &tv );
	if( nret != 0 )
		log_warn( g_event_base->log, "event dispatch %d", nret );

	event_base_timer_process( base, tnow );

	for( index = 0; index < base->event_active_count; index++ )
	{
		struct event_s *ev;
		
		ev = base->event_active_array[index];
		base->event_active_array[index] = NULL;
		event_del( ev );
		
		ev->handle( ev->fd, ev->flags, ev->arg );		
	}
	
	base->event_active_count = 0;
}



